
/**
 *  \file locale.hpp
 *  \brief Curses++ locale header
 *
 *  This contains all the various specializations of the standard library
 *  locale facets.
 *
 *  \if LICENSE
 *  Curses++ - Free C++ wrapper to the curses library.
 *  Copyright (C) 2007 Sean Hunt
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330,
 *  Boston, MA 02111-1307, USA.
 *  \endif
 */

#ifndef CURSESPLUSPLUS_LOCALE_HPP
#define CURSESPLUSPLUS_LOCALE_HPP

#include <cursesplusplus/character.hpp>

#include <iosfwd>

/// Standard namespace; some specializations are made.
/**
 *  Normally, defining anything in the %std namespace is illegal. However, it is
 *  permitted to explicitly specialize a template in %std, as is done multiple
 *  times here.
 */
namespace std
{
	using namespace curses;

	/// Specialization of char_traits for character.
	/**
	 *  For those of you not familiar with string mechcanics, this
	 *  specialization makes classes like std::string work with custom
	 *  %character types.
	 */
	template <> class char_traits <character>
	{
		public:

			typedef character char_type;
			typedef struct { character c; bool eof; } int_type;
			typedef character::iostate * state_type;
			typedef fpos<state_type> pos_type;
			typedef streamoff off_type;

			static bool eq (char_type c, char_type d)
			{
				return c == d;
			}

			static bool lt (char_type c, char_type d)
			{
				return c < d;
			}

			static int compare (const char_type* p, const char_type* q, size_t n)
			{
				for (const char_type* r = p; size_t(r - p) < n; ++r, ++q)
					if (lt(*r, *q))
						return r - p;
					else if (!eq(*r, *q))
						return p - r;
				return 0;
			}

			static size_t length (const char_type* c)
			{
				size_t count = 0;
				while (c->c)
				{
					c++;
					count++;
				}
				return count;
			}

			static const char_type* find (const char_type* p, size_t n, char_type c)
			{
				for (const char_type* r = p; size_t(r - p) < n; ++r)
					if (eq(*r, c))
						return r;
				return NULL;
			}
	
			static char_type* move (char_type* s, const char_type* p, size_t n)
			{
				if (s == p)
					return s;
				// Reverse ordering due to overlap.
				if (s > p && s < p + n)
				{
					p += n - 1;
					for (char_type * r = s + n - 1; r > s; --r, --p)
						assign (*r, *p);
					assign (*s, *p);
				}
				else
					for (char_type * r = s; size_t(r - s) < n; ++r, ++p)
						assign (*r, *p);
				return s;
			}
	
			static char_type * copy (char_type * const & s, const char_type * p, size_t & n)
			{
				for (char_type* r = s; size_t(r - s) < n; ++r, ++p)
					assign (*r, *p);
				return s;
			}
	
			static char_type& assign (char_type& r, char_type d)
			{
				return r = d;
			}
	
			static char_type* assign (char_type* s, size_t n, char_type& c)
			{
				for (char_type* r = s; size_t(r - s) < n; ++r)
					assign(*r, c);
				return s;
			}
	
			static int_type not_eof (int_type e)
			{
				int_type i = {e.c, false};
				return i;
			}
	
			static char_type to_char_type (int_type e)
			{
				return e.c;
			}
	
			static int_type to_int_type (char_type c)
			{
				int_type i = {c, false};
				return i;
			}
	
			static bool eq_int_type (int_type e, int_type f)
			{
				return (e.eof == f.eof) && (e.c == f.c);
			}
	
			static int_type eof ()
			{
				static int_type eof = { char_type(), true };
				return eof;
			}
	};

	/**
	 *  \defgroup CTypeSpecializations Specializations for std::ctype<character>
	 *
	 *  Each of these functions simply forwards to the appropriate function in ctype<char>
	 *
	 *  \{
	 */

	#ifdef __GNUC__
	/// GCC doesn't provide an implementation for the destructor for some reason.
	template <> inline ctype<character>::~ctype ()
	{}
	#endif

	template <> inline bool ctype<character>::do_is (mask m, character c) const
	{
		return use_facet<ctype<char> >(locale()).is(m, c.c);
	}

	template <> inline const character* ctype<character>::do_is (const character* low, const character* high, mask* vec) const
	{
		const ctype<char> & c = use_facet<ctype<char> >(locale());
		for (--high; high > low; --high)
			c.is(&(high->c), &(high->c), vec + (high - low));
		c.is(&(high->c), &(high->c), vec + (high - low));
		return high;
	}

	template <> inline const character * ctype<character>::do_scan_is (mask m, const character* low, const character* high) const
	{
		for (; low < high; ++low)
			if (do_is(m, *low))
				return low;
		return high;
	}

	template <> inline const character * ctype<character>::do_scan_not (mask m, const character* low, const character* high) const
	{
		for (; low < high; ++low)
			if (!do_is(m, *low))
				return low;
		return high;
	}

	template <> inline character ctype<character>::do_toupper (character c) const
	{
		return character(c).set_char(use_facet<ctype<char> >(locale()).toupper(c.c));
	}

	template <> inline const character * ctype<character>::do_toupper (character* low, const character* high) const
	{
		const ctype<char> & c = use_facet<ctype<char> >(locale());
		for (; low < high; ++low)
			low->c = c.toupper(low->c);
		return high;
	}

	template <> inline character ctype<character>::do_tolower (character c) const
	{
		return character(c).set_char(use_facet<ctype<char> >(locale()).tolower(c.c));
	}

	template <> inline const character * ctype<character>::do_tolower (character* low, const character* high) const
	{
		const ctype<char> & c = use_facet<ctype<char> >(locale());
		for (; low < high; ++low)
			low->c = c.tolower(low->c);
		return high;
	}

	template <> inline character ctype<character>::do_widen (char c) const
	{
		return character(c);
	}

	template <> inline const char * ctype<character>::do_widen (const char* low, const char* high, character* dest) const
	{
		for (; low < high; ++low, ++dest)
			*dest = character(*low);
		return high;
	}

	template <> inline char ctype<character>::do_narrow (character c, char dfault) const
	{
		UNUSED_PARAM(dfault);
		return c.c;
	}

	template <> inline const character * ctype<character>::do_narrow (const character* low, const character* high, char dfault, char* dest) const
	{
		UNUSED_PARAM(dfault);
		for (; low < high; ++low, ++dest)
			*dest = low->c;
		return high;
	}

	/// \}

	/**
	 *  \defgroup CodeCvtSpecializations Specializations for std::codecvt<character>
	 *
	 *  Each of these functions simply forwards to the appropriate function in codecvt<char>
	 *
	 *  \{
	 */

	template <> inline codecvt_base::result codecvt<character, char, character::iostate>::do_out(character::iostate& state, const character* from, const character* from_end, const character*& from_next, char* to, char* to_limit, char*& to_next) const
	{
		while (from < from_end && to < to_limit)
		{
			switch (state)
			{
				// First 8 bits: the character
				case character::none:
					*to++ = from->c;
					state = character::colors;
					break;
				// Next 8 bits: aabbbfff, where a are the smallest two attribute bits, b is the background color, and f is the foreground color.
				case character::colors:
					*to = from->fg;
					*to &= from->bg << 3;
					*to++ &= from->attr << 6;
					state = character::attributes;
					break;
				// Last 8 bits: the remainder of the attributes.
				case character::attributes:
					*to++ = from++->attr >> 2;
					state = character::none;
					break;
			}
		}

		to_next = to;
		from_next = from;

		if (from == from_end)
			// implies state == character::none
			return ok;
		else if (to == to_limit)
			// ran out of destination space.
			return partial;
		else
			// shouldn't be possible
			return error;
	}

	template <> inline codecvt_base::result codecvt<character, char, character::iostate>::do_in(character::iostate& state, const char* from, const char* from_end, const char*& from_next, character* to, character* to_limit, character*& to_next) const
	{
		while (from < from_end && to < to_limit)
		{
			switch (state)
			{
				case character::none:
					to->c = *from++;
					state = character::colors;
					break;
				case character::colors:
					to->fg = static_cast<character::color>(*from & 7);
					to->bg = static_cast<character::color>(*from >> 3 & 7);
					to->attr = static_cast<character::attribute>(*from++ >> 6 & 2);
					state = character::attributes;
					break;
				case character::attributes:
					// Cast required or else we lose data due to *from++ << 2 being a char.
					to++->attr &= static_cast<character::attribute>(*from++ << 2);
					break;
			}
		}

		to_next = to;
		from_next = from;

		if (to == to_limit)
			// ran out of destination space.
			return partial;
		else if (from == from_next)
		{	// ran out of source elements.
			if (state == character::none)
				// Finished conversion
				return ok;
			else
				// Incomplete conversion
				return partial;
		}
		else
			// Shouldn't happen
			return error;
	}

	template <> inline codecvt_base::result codecvt<character, char, character::iostate>::do_unshift (character::iostate& state, char* to, char* to_limit, char*& to_next) const
	{
		switch (state)
		{
			case character::none:
				// nothing to do.
				to_next = to;
				return ok;
			case character::colors:
				// Add zeroes to bring it up.
				if (to == to_limit)
				{
					to_next = to;
					return partial;
				}
				*to++ = 0;
				state = character::attributes;
				// fall-through
			case character::attributes:
				if (to == to_limit)
				{
					to_next = to;
					return partial;
				}
				*to++ = 0;
				state = character::attributes;
				return ok;
		}
		// shouldn't happen
		return error;
	}

	template <> inline int codecvt<character, char, character::iostate>::do_encoding () const
	{
		return 3;
	}

	template <> inline bool codecvt<character, char, character::iostate>::do_always_noconv () const throw()
	{
		return true;
	}

	template <> inline int codecvt<character, char, character::iostate>::do_length(character::iostate& state, const char* from, const char * from_end, size_t max) const
	{
		size_t count = from_end - from;
		switch (state)
		{
			case character::none:;
			case character::colors: ++++count;
			case character::attributes: ++count;
		}
		state = character::none;
		return count > max*3 ? count : max*3;
	}

	template <> inline int codecvt<character, char, character::iostate>::do_max_length() const throw()
	{
		return 3;
	}
	/// \}
}

#endif
